/*
 * @Description  :
 * @Version      : 0.1
 * @Company      : V-Think Development Team
 * @Author       : KiraVerSace@yeah.net
 * @Date         : 2025-01-04 00:12:38
 */
#include <appPeriphRun.h>

DAC8563 dac856X;

void dacOutputWrite(uint8_t channel, uint16_t requestedMV);

osThreadId periphRunTaskHandle;
void periphRunTask(void *pvParameters)
{
    UNUSED(pvParameters);
    xLog.info("periphRunTask...[%d]\r\n", uxTaskGetStackHighWaterMark(NULL));

    XSensorDataT xSensorData;

    digitalWrite(RELAY_CH1, LOW);
    digitalWrite(RELAY_CH2, LOW);
    pinMode(LED_BUILTIN_G, OUTPUT);
    pinMode(RELAY_CH1, OUTPUT);
    pinMode(RELAY_CH2, OUTPUT);
    digitalWrite(LED_BUILTIN_G, HIGH);
    digitalWrite(RELAY_CH1, LOW);
    digitalWrite(RELAY_CH2, LOW);

    const TickType_t ledOnTicks  = pdMS_TO_TICKS(1000U);
    const TickType_t ledOffTicks = pdMS_TO_TICKS(500U);
    TickType_t ledNextToggle     = xTaskGetTickCount() + ledOnTicks;
    bool ledState                = true;

    dac856X.begin(DAC856X_CS, DAC856X_DIN, DAC856X_SCLK);

    for (;;)
    {
        if (xQueueReceive(xSensorDataQueue, &xSensorData, (TickType_t)100) == pdPASS)
        {
            xLog.info("xSensorDataQueue receive success...CH1=[%d]mv CH2=[%d]mv Relay=[%d]\r\n",
                                            xSensorData.mvCH1, xSensorData.mvCH2, xSensorData.relayOutput);

            dacOutputWrite(1, xSensorData.mvCH1);
            dacOutputWrite(2, xSensorData.mvCH2);

            if (bitRead(xSensorData.relayOutput, 0))
            {
                digitalWrite(RELAY_CH1, HIGH);
            }
            else
            {
                digitalWrite(RELAY_CH1, LOW);
            }

            if (bitRead(xSensorData.relayOutput, 1))
            {
                digitalWrite(RELAY_CH2, HIGH);
            }
            else
            {
                digitalWrite(RELAY_CH2, LOW);
            }
        }
        else
        {
            // xLog.warning("xSensorDataQueue no message...\r\n");
        }

        const TickType_t now = xTaskGetTickCount();
        if (now >= ledNextToggle)
        {
            ledState = !ledState;
            digitalWrite(LED_BUILTIN_G, ledState ? HIGH : LOW);
            ledNextToggle = now + (ledState ? ledOnTicks : ledOffTicks);
        }

        watchdogReport(WATCHDOG_SRC_PERIPH);
        vTaskDelay(10);
    }
}

uint16_t dacChannelCalibration(uint8_t channel, uint16_t targetMV)
{
    if (targetMV == 0)
    {
        xLog.trace("DAC CH%d Calibration: Target=0mV → Output=0mV (Zero bypass)\r\n", channel);
        return 0;
    }

    const size_t   lastIndex   = DAC_CALIBRATION_TABLE_SIZE - 1;
    const uint16_t actualFirst = (channel == 2) ? dacCalibrationTable[0].outputCH2 : dacCalibrationTable[0].outputCH1;
    const uint16_t actualLast  = (channel == 2) ? dacCalibrationTable[lastIndex].outputCH2 : dacCalibrationTable[lastIndex].outputCH1;

    xLog.trace("DAC CH%d Calibration: Target=%dmV, Range[%dmV-%dmV]\r\n",
              channel, targetMV, actualFirst, actualLast);

    if (targetMV <= actualFirst)
    {
        const uint16_t actualNext = (channel == 2) ? dacCalibrationTable[1].outputCH2 : dacCalibrationTable[1].outputCH1;
        const uint16_t inputFirstMV = dacCalibrationTable[0].inputMV;
        const uint16_t inputNextMV = dacCalibrationTable[1].inputMV;
        const float denominator = static_cast<float>(actualNext) - static_cast<float>(actualFirst);
        float corrected = static_cast<float>(inputFirstMV);
        if (denominator != 0.0f)
        {
            const float slope = (static_cast<float>(inputNextMV) - static_cast<float>(inputFirstMV)) / denominator;
            corrected += (static_cast<float>(targetMV) - static_cast<float>(actualFirst)) * slope;
        }
        if (corrected <= 0.0f)
        {
            xLog.trace("DAC CH%d Calibration: Lower range corrected ≤0, clamped to 0mV\r\n", channel);
            return 0;
        }
        if (corrected >= 10000.0f)
        {
            xLog.trace("DAC CH%d Calibration: Lower range corrected ≥10V, clamped to 10000mV\r\n", channel);
            return 10000;
        }
        const uint16_t result = static_cast<uint16_t>(corrected + 0.5f);
        xLog.trace("DAC CH%d Calibration: Lower range extrapolation → %dmV\r\n", channel, result);
        return result;
    }

    if (targetMV >= actualLast)
    {
        const uint16_t actualPrev = (channel == 2) ? dacCalibrationTable[lastIndex - 1].outputCH2 : dacCalibrationTable[lastIndex - 1].outputCH1;
        const uint16_t inputPrevMV = dacCalibrationTable[lastIndex - 1].inputMV;
        const uint16_t inputLastMV = dacCalibrationTable[lastIndex].inputMV;
        const float denominator = static_cast<float>(actualLast) - static_cast<float>(actualPrev);
        float corrected = static_cast<float>(inputLastMV);
        if (denominator != 0.0f)
        {
            const float slope = (static_cast<float>(inputLastMV) - static_cast<float>(inputPrevMV)) / denominator;
            corrected += (static_cast<float>(targetMV) - static_cast<float>(actualLast)) * slope;
        }
        if (corrected <= 0.0f)
        {
            xLog.trace("DAC CH%d Calibration: Upper range corrected ≤0, clamped to 0mV\r\n", channel);
            return 0;
        }
        if (corrected >= 10000.0f)
        {
            xLog.trace("DAC CH%d Calibration: Upper range corrected ≥10V, clamped to 10000mV\r\n", channel);
            return 10000;
        }
        const uint16_t result = static_cast<uint16_t>(corrected + 0.5f);
        xLog.trace("DAC CH%d Calibration: Upper range extrapolation → %dmV\r\n", channel, result);
        return result;
    }

    uint16_t inputPrevMV = dacCalibrationTable[0].inputMV;
    uint16_t actualPrev = actualFirst;
    for (size_t i = 0; i < lastIndex; ++i)
    {
        const uint16_t inputCurrentMV = dacCalibrationTable[i + 1].inputMV;
        const uint16_t actualCurrent = (channel == 2) ? dacCalibrationTable[i + 1].outputCH2 : dacCalibrationTable[i + 1].outputCH1;
        if (targetMV <= actualCurrent)
        {
            const float denominator = static_cast<float>(actualCurrent) - static_cast<float>(actualPrev);
            float corrected = static_cast<float>(inputPrevMV);
            if (denominator != 0.0f)
            {
                const float ratio = (static_cast<float>(targetMV) - static_cast<float>(actualPrev)) / denominator;
                corrected = static_cast<float>(inputPrevMV) + ratio * (static_cast<float>(inputCurrentMV) - static_cast<float>(inputPrevMV));
                xLog.trace("DAC CH%d Calibration: Interpolation between %dmV-%dmV (actual %dmV-%dmV)\r\n",
                          channel, inputPrevMV, inputCurrentMV, actualPrev, actualCurrent);
            }
            if (corrected <= 0.0f)
            {
                xLog.trace("DAC CH%d Calibration: Interpolated corrected ≤0, clamped to 0mV\r\n", channel);
                return 0;
            }
            if (corrected >= 10000.0f)
            {
                xLog.trace("DAC CH%d Calibration: Interpolated corrected ≥10V, clamped to 10000mV\r\n", channel);
                return 10000;
            }
            const uint16_t result = static_cast<uint16_t>(corrected + 0.5f);
            xLog.trace("DAC CH%d Calibration: Interpolation → %dmV\r\n", channel, result);
            return result;
        }

        inputPrevMV = inputCurrentMV;
        actualPrev = actualCurrent;
    }

    if (targetMV >= 10000)
    {
        xLog.trace("DAC CH%d Calibration: Target ≥10V, clamped to 10000mV\r\n", channel);
        return 10000;
    }
    xLog.trace("DAC CH%d Calibration: No calibration applied, passthrough %dmV\r\n", channel, targetMV);
    return targetMV;
}

void dacOutputWrite(uint8_t channel, uint16_t requestedMV)
{
    uint16_t targetMV = requestedMV;
    if (targetMV > 10000)
    {
        targetMV = 10000;
        xLog.warning("DAC CH%d: Requested %dmV exceeded limit, clamped to %dmV\r\n",
                    channel, requestedMV, targetMV);
    }

    if (channel == 1)
    {
        const uint16_t calibrated = dacChannelCalibration(1, targetMV);
        const uint16_t dacValue = static_cast<uint16_t>(map(calibrated, 0, 10000, 0, 65535));
        xLog.trace("DAC CH1: Target=%dmV → Calibrated=%dmV → DacValue=%d\r\n",
                  targetMV, calibrated, dacValue);
        dac856X.setDACOutVoltage(1, dacValue);
    }
    else if (channel == 2)
    {
        const uint16_t calibrated = dacChannelCalibration(2, targetMV);
        const uint16_t dacValue = static_cast<uint16_t>(map(calibrated, 0, 10000, 0, 65535));
        xLog.trace("DAC CH2: Target=%dmV → Calibrated=%dmV → DacValue=%d\r\n",
                  targetMV, calibrated, dacValue);
        dac856X.setDACOutVoltage(2, dacValue);
    }
    else
    {
        const uint16_t calibratedCh1 = dacChannelCalibration(1, targetMV);
        const uint16_t calibratedCh2 = dacChannelCalibration(2, targetMV);
        const uint16_t dacValueCh1 = static_cast<uint16_t>(map(calibratedCh1, 0, 10000, 0, 65535));
        const uint16_t dacValueCh2 = static_cast<uint16_t>(map(calibratedCh2, 0, 10000, 0, 65535));
        xLog.trace("DAC BOTH: Target=%dmV → CH1: Calibrated=%dmV/DacValue=%d, CH2: Calibrated=%dmV/DacValue=%d\r\n",
                  targetMV, calibratedCh1, dacValueCh1, calibratedCh2, dacValueCh2);
        dac856X.setDACOutVoltage(1, dacValueCh1);
        dac856X.setDACOutVoltage(2, dacValueCh2);
    }
}
